#include "ActionLogin.h"
#include "ActionBase.h"
#include "context.h"
#include "util.h"
#include "streamdata.h"
#include "Md5.h"
#include "json.h"
#include "account_service.h"
#include "agency_service.h"
#include "gameinfo_service.h"
#include "agencymgr.h"
#include "gameinfomgr.h"


std::string formatDateAsYYMMD(std::int32_t offset = 0) {

    // 获取当前系统时间  
    auto now = std::chrono::system_clock::now(); 
    // 将时间转换为time_t类型，以便转换为tm结构体,utf-4
    std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(4));
	now_c += offset;
    // 获得utc时间
    std::tm* now_tm = std::gmtime(&now_c);
    // 格式化日期（注意这里没有使用前导零）  
    int year = now_tm->tm_year + 1900; // tm_year是从1900年开始的年份  
    int month = now_tm->tm_mon + 1;    // tm_mon是从0开始的月份  
    int day = now_tm->tm_mday;        // tm_mday是日期（1-31） 
  
    // 输出格式为YYMMDD，但DD没有前导零  
    std::ostringstream oss;  
    oss<< std::setfill('0') << std::setw(2) << year % 100  // 年份的后两位  
              << std::setw(2) << month                            // 月份  
              << day;                                            // 日期  

    // 使用put_time来格式化输出  
    //std::cout << "Current UTC-4 time: " << std::put_time(now_tm, "%Y-%m-%d %H:%M:%S") << '\n';
    return oss.str();  
}

std::string GetKeyG(const std::string& agency_id, const std::string& agency_key, std::int32_t offset)
{
    std::string date_str = formatDateAsYYMMD(offset);
    std::string keyG = date_str + agency_id + agency_key;
    log_debug("GetKey keyG =  %s", keyG.c_str());
    std::string sign_md5 = MD5(keyG).toStr();
    log_debug("GetKey after MD5 keyG =  %s", sign_md5.c_str());
    return sign_md5;
}



static bool __init__ = ActionLogin::Init();

bool ActionLogin::Init()
{
    Ctx.msg_router.BindHttpHandle("post", "/api/singleWallet/Login", ActionLogin::HandleLogin);
    Ctx.msg_router.BindHttpHandle("post", "/api/singleWallet/LoginWithoutRedirect", ActionLogin::HandleLogin);
    Ctx.msg_router.BindHttpHandle("get", "/api/singleWallet/Login", ActionLogin::HandleLogin);
    Ctx.msg_router.BindHttpHandle("get", "/api/singleWallet/LoginWithoutRedirect", ActionLogin::HandleLogin);

    AgencyService::Instance()->ListenOnAgencyGet(AgencyService::ACCESS_LOGIN, ActionLogin::OnAgencyGet);
    AccountService::Instance()->ListenOnGetToken(AccountService::ACCESS_LOGIN, ActionLogin::OnGenLoginToken);
    GameInfoService::Instance()->ListenOnGameinfo(GameInfoService::ACCESS_LOGIN, ActionLogin::OnGameinfoGet);

    return true;
}

void ActionLogin::ResponseLogin(std::uint32_t request_id, const std::uint64_t request_time, Error err, const std::string& url, std::uint32_t display_mode)
{
	std::uint64_t response_time = CTools::GetUnixTimeMs();
    Json::Value json;
    json["ErrorCode"] = ToCode(err);
    json["Message"] = ToString(err);
    json["Data"] = url;
    json["DisplayMode"] = display_mode;
    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";
	std::string body = CTools::ToStr(json);
	log_debug("request_id:%u,body:%s,request_time:%lu,response_time:%lu,us_cost:%lu", request_id, body.c_str(), request_time, response_time, response_time-request_time);
    Ctx.msg_router.httpServerBack(request_id, 200, heads, std::move(body));
}

void ActionLogin::HandleLogin(HttpData&& data)
{
    Error err = SUCCESS;
	std::uint64_t request_time = CTools::GetUnixTimeMs();
    do
    {
        log_debug("request_id:%d,ip:%s,target:%s,body:%s", data.request_id, data.ip.c_str(), data.target.c_str(), data.body.c_str());

        if (data.params.end() == data.params.find("Token")) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params.end() == data.params.find("GameId")) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; } 
        if (data.params.end() == data.params.find("Lang")) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params.end() == data.params.find("AgentId")) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params.end() == data.params.find("Key")) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params["Token"].empty()) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params["GameId"].empty() || (std::string::npos != data.params["GameId"].find(','))) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params["Lang"].empty() || (std::string::npos != data.params["Lang"].find(','))) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if (data.params["AgentId"].empty()) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
        if ((data.params["Key"].size() <= 12)) { log_error("param error, %s", data.target.c_str()); err = INVALID_KEY; break; }
		
		// 解码 拓展参数
		std::string extern_json = "";
		if (data.params.find("extern_json") != data.params.end() && !data.params["extern_json"].empty())
		{
			extern_json = data.params["extern_json"];
			extern_json = CTools::Base64(extern_json, false);
		}
		log_debug("param extern_json=%s;", extern_json.c_str());

        
        std::uint32_t gameid = ::strtoul(data.params["GameId"].c_str(), NULL, 0);
        if (0 == gameid)
        {
            log_error("param error, %s", data.target.c_str());
            err = INVALID_KEY; 
            break;
        }
        // 获取代理信息
        stAgency* agency = AgencyMgr::Instance()->Get(data.params["AgentId"]);
        if (agency != nullptr)
        {
            agencyProc(data.request_id, request_time, data.ip, data.params["Token"], gameid, data.params["Lang"], data.params["AgentId"]
                , data.params["Key"], data.params["HomeUrl"], data.params["Platform"], ::atoi(data.params["DisableFullScreen"].c_str())
                , agency->name
                , agency->id
                , agency->language_list
                , agency->currency
				, agency->currency_unit_multi
                , agency->currency_label
                , agency->white_ips
                , agency->encode_key
                , agency->call_url
                , agency->state
                , agency->agent_url
                , agency->gamelist
				, extern_json
                );
        }
        else
        {
			// 如果 获取对应agency信息
			StreamData stream_data;
			StreamDataWriter write(stream_data);
			write.WriteInt(data.request_id);
			write.WriteInt64(request_time);
			write.WriteString(data.ip);
			write.WriteString(data.params["Token"]);
			write.WriteInt(gameid);
			write.WriteString(data.params["Lang"]);
			write.WriteString(data.params["AgentId"]);
			write.WriteString(data.params["Key"]);
			write.WriteString(data.params["HomeUrl"]);
			write.WriteString(data.params["Platform"]);
			write.WriteInt(::atoi(data.params["DisableFullScreen"].c_str()));
			write.WriteString(extern_json);

            // 获取代理信息
			if (!AgencyService::Instance()->HandleGetAgent(data.params["AgentId"], std::move(stream_data.Data()), AgencyService::ACCESS_LOGIN))
			{
				log_error("HandleGetAgent failed, target:%s", data.target.c_str());
				err = FAILED; 
                break;
			}
        }
        return;
    } while (false);
    ResponseLogin(data.request_id, request_time, err);
}

void ActionLogin::OnAgencyGet(stAgency* p_agency, std::int32_t err_code, std::string&& trans)
{
    log_debug("enter");

    Error err = SUCCESS;

    StreamData stream_data(std::move(trans));
    StreamDataReader reader(stream_data);
    std::uint32_t request_id = reader.ReadInt();
	std::uint64_t request_time = reader.ReadInt64();
    do
    {
        //-------------------------校验参数------------------------------
        if (p_agency == nullptr)
        {
            log_error("agency is null,err_code:%d", err_code);
            err = SITE_MASTER_NOT_FOUND; 
            break;
        }
        if (err_code != 0)  // 代理 未 找到
        {
            log_error("get p_agency:%s failed,err_code:%d", p_agency->ToString().c_str(), err_code);
            err = SITE_MASTER_NOT_FOUND; 
            break;
        }
		log_debug("agency:%s", p_agency->ToString().c_str());
        
		std::string ip = reader.ReadString();
		std::string token = reader.ReadString();
		std::uint32_t gameid = reader.ReadInt();
		std::string lang = reader.ReadString();
		std::string agency = reader.ReadString();
		std::string key = reader.ReadString();
		std::string home_url = reader.ReadString();
		std::string platform = reader.ReadString();
		std::int32_t fullscreen = reader.ReadInt();
		std::string extern_json = reader.ReadString();

		agencyProc(request_id, request_time, ip, token, gameid, lang, agency
			, key, home_url, platform, fullscreen
            , p_agency->name
            , p_agency->id
			, p_agency->language_list
            , p_agency->currency
			, p_agency->currency_unit_multi
			, p_agency->currency_label
            , p_agency->white_ips
            , p_agency->encode_key
            , p_agency->call_url
            , p_agency->state
            , p_agency->agent_url
            , p_agency->gamelist
			, extern_json
            );

        return;
    } while (false);
    ResponseLogin(request_id, request_time, err);
}

void ActionLogin::agencyProc(const std::uint32_t request_id, const std::uint64_t request_time, const std::string& ip, std::string& token, const std::uint32_t gameid, const std::string& lang
        , std::string& agency, const std::string& key, std::string& homeurl, std::string& platform, const int disablefullscreen
        , const std::string& srvagency, const std::uint32_t agengcy_id, const std::set<std::string>& srvlang, const std::string& currency, const std::int64_t& currency_unit_multi, const std::string& currency_label, const std::set<std::string>& white_ips
        , const std::string& encode_key, const std::string& url, const int state, const std::vector<std::string>& agent_url_list, const std::set<std::uint32_t>& gamelist, const std::string& extern_json)
{
    Error err = SUCCESS;
	do
	{
		if (srvagency.compare(agency) != 0)  // 代理号 不一致
		{
			log_error("agency not match, agency(param):%s,agency(system):%s", agency.c_str(), srvagency.c_str());
			err = SITE_MASTER_NOT_MISMATCH; 
            break;
		}

		if (srvlang.end() == srvlang.find(lang))  // 语言不一致
		{
			log_error("lang not match, agency:%s, lang(param):%s,lang(system):%s", agency.c_str(), lang.c_str(), CTools::ToGroupString(srvlang).c_str());
			err = FAILED; 
            break;
		}
        // ip 放到 运维层面 去控制 区分?
		// if (white_ips.end() == std::find(white_ips.begin(), white_ips.end(), ip))
		// {
		// 	log_error("ip not match,agency:%s, ip(param):%s,ip(system):%s", agency.c_str(), ip.c_str(), CTools::ToString(white_ips).c_str());
		// 	err = FAILED; 
        //     break;
		// }
		if (gamelist.end() == gamelist.find(gameid))
		{
			log_error("game not match,agency:%s, game(param):%u,gamelist(system):%s", agency.c_str(), gameid, CTools::ToGroupString(gamelist).c_str());
			err = FAILED; 
            break;
		}
		if (state != 1)
		{
			log_error("agency no valid, state:%d,agency:%s", state, agency.c_str());
			err = FAILED; 
            break;
		}
        std::string agent_url = CTools::ToString(agent_url_list);
        if (agent_url.empty())
        {
            log_error("agency agent_url is empty,%s", agency.c_str());
            err = FAILED; break;
        }

		// 校验 key
		std::stringstream queryStr;
		queryStr << "Token=" << token << "&GameId=" << gameid << "&Lang=" << lang << "&AgentId=" << agency;
		bool match = false;
		std::string trim_key = key.substr(0, key.size() - 6).substr(6);
		for (auto offset : { 0, -86400, 86400 })  // 多判断前后1天, 兼容时区
		{
			std::string queryMd5 = queryStr.str() + GetKeyG(srvagency, encode_key, offset);
			std::string md5Str = MD5(queryMd5).toStr();
			if (trim_key.compare(md5Str) == 0)
			{
				match = true;
				break;
			}
			log_error("md5Str not match, md5Str(param):%s,md5Str(system):%s", trim_key.c_str(), md5Str.c_str());
		}
		if (!match)
		{
			log_error("match failed, agency:%s, key:%s,queryStr:%s", agency.c_str(), key.c_str(), queryStr.str().c_str());
			err = INVALID_KEY; 
            break;
		}


		stGameInfo* gameinfo = GameInfoMgr::Instance()->Get(agengcy_id, gameid);
		if (gameinfo != nullptr )
		{
			if (gameinfo->state != 1)
			{
				log_error("gameinfo state not 1,%s", gameinfo->ToString().c_str());
				err = FAILED; 
				break;
			}
			gameinfoProc(request_id, request_time, url, gameinfo->lauch_url, gameinfo->game, 
				agency, agengcy_id, token, homeurl, platform, gameinfo->display_mode, disablefullscreen, 
				currency, currency_unit_multi, currency_label, agent_url, lang, extern_json);
		}
		else
		{
			StreamData stream_lauch_url;
			StreamDataWriter write_lauch_url(stream_lauch_url);
			write_lauch_url.WriteInt(request_id);
			write_lauch_url.WriteInt64(request_time);
			write_lauch_url.WriteString(url);
			write_lauch_url.WriteInt(agengcy_id);
			write_lauch_url.WriteString(agency);
			write_lauch_url.WriteString(token);
			write_lauch_url.WriteString(homeurl);
			write_lauch_url.WriteString(platform);
			write_lauch_url.WriteInt(disablefullscreen);
			//write_lauch_url.WriteInt64(llbalance);
			//write_lauch_url.WriteString(username);
			write_lauch_url.WriteString(currency);
			write_lauch_url.WriteInt64(currency_unit_multi);
			write_lauch_url.WriteString(currency_label);
			write_lauch_url.WriteString(agent_url);
			write_lauch_url.WriteString(lang);
			write_lauch_url.WriteString(extern_json);

			if (!GameInfoService::Instance()->HandleGameInfo(agengcy_id, gameid, std::move(stream_lauch_url.Data()), GameInfoService::ACCESS_LOGIN))
			{
				log_error("HandleGameInfo failed, gameid:%u", gameid);
				err = FAILED;
				break;
			}
		}
        return;
	} while (false);
	ResponseLogin(request_id, request_time, err);
}

void ActionLogin::OnGameinfoGet(stGameInfo* gameinfo, std::int32_t err_code, std::string&& trans)
{
    log_debug("enter");

    Error err = SUCCESS;

    StreamData stream_data(std::move(trans));
    StreamDataReader reader(stream_data);
    std::uint32_t request_id = reader.ReadInt();
	std::uint64_t request_time = reader.ReadInt64();
 
    do
    {
        if (gameinfo == nullptr)
        {
            log_error("gameinfo is null,err_code:%d", err_code);
            err = FAILED; 
            break;
        }
        if (err_code != 0)
        {
            log_error("get gameinfo:%s failed,err_code:%d", gameinfo->ToString().c_str(), err_code);
            err = FAILED; 
            break;
        }
		if (gameinfo->state != 1)
		{
			log_error("gameinfo state not 1,%s", gameinfo->ToString().c_str());
			err = FAILED; 
			break;
		}
        log_debug("gameinfo:%s", gameinfo->ToString().c_str());
        
		std::string auth_url = reader.ReadString();
		std::uint32_t agency_id = reader.ReadInt();
		std::string agency = reader.ReadString();
		std::string token = reader.ReadString();
		std::string homeurl = reader.ReadString();
		std::string platform = reader.ReadString();
		std::int32_t fullscreen = reader.ReadInt();
		//std::int64_t balance = reader.ReadInt64();
		//std::string username = reader.ReadString();
		std::string currency = reader.ReadString();
		std::int64_t currency_unit_multi = reader.ReadInt64();
		std::string currency_label = reader.ReadString();
        std::string agent_url = reader.ReadString();
        std::string lang = reader.ReadString();
		std::string extern_json = reader.ReadString();
		
        gameinfoProc(request_id, request_time, auth_url, gameinfo->lauch_url, gameinfo->game, 
			agency, agency_id, token, homeurl, platform, gameinfo->display_mode, fullscreen, 
			currency, currency_unit_multi, currency_label, agent_url, lang, extern_json);
        return ;
    } while (false);
    ResponseLogin(request_id, request_time, err);
}

void ActionLogin::gameinfoProc(const std::uint32_t request_id, const std::uint64_t request_time, const std::string& auth_url, const std::string& lauch_url, const std::uint32_t gameid
	, std::string& agency, const std::uint32_t agengcy_id, std::string& token, std::string& homeurl, std::string& platform, std::uint32_t display_mode
	, const std::int32_t fullscreen, const std::string& currency, const std::int64_t& currency_unit_multi, const std::string& currency_label, std::string& agent_url, const std::string& lang, const std::string& extern_json){
	Error err = SUCCESS;
    do
    {
		static std::map<std::string, std::string> s_http_json_heads = { {"Content-Type","application/json"} };
		Json::Value http_req;
		http_req["reqId"] = CTools::genTransId();
		http_req["token"] = token;

		std::uint64_t http_req_time = CTools::GetUnixTimeMs();
		std::string json_string = CTools::ToStr(http_req);
		std::string urlpath = auth_url + "/auth";
		std::uint32_t event_id = 0;
		log_debug("request_id:%u,url:%s,param:%s,,http_req_time:%lu", request_id, urlpath.c_str(), json_string.c_str(), http_req_time);
		if (!Ctx.msg_router.sendHttpPOST(urlpath, json_string, s_http_json_heads, event_id) || (event_id == 0))
		{
			err = API_AUTH_FAILED;
			log_error("net post failed,url:%s,param:%s", urlpath.c_str(), json_string.c_str());
			break;
		}

		// 添加回调监听
		Ctx.msg_router.AddCurlEvent(event_id, [request_id, request_time, http_req_time,
			gameid, agengcy_id, agency = std::move(agency), token = std::move(token),lang,
			currency, currency_unit_multi, currency_label,
			homeurl = std::move(homeurl), platform = std::move(platform), fullscreen, lauch_url, display_mode,
			url1 = std::move(urlpath), json_string = std::move(json_string), agent_url = std::move(agent_url), extern_json]
			(std::string& body, bool is_ok) 
			{
				std::uint64_t http_rsp_time = CTools::GetUnixTimeMs();
				Error err1 = SUCCESS;
				Json::Value http_resp;
				do
				{
					log_debug("request_id:%u,url:%s,param:%s,resp_body:%s,http_req_time:%lu,http_rsp_time:%lu,cost:%lu us", request_id, url1.c_str(), json_string.c_str(), body.c_str(), http_req_time, http_rsp_time, http_rsp_time-http_req_time);
					// 接收用户平台 操作
					if (!is_ok)
					{
						err1 = API_AUTH_FAILED;
						log_error("time out,url:%s,param:%s", url1.c_str(), json_string.c_str());
						break;
					}

					// 解析返回格式
					if (!CTools::FromStr(http_resp, body))
					{
						err1 = API_AUTH_FAILED;
						log_error("resp from json failed, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}
					if (!(http_resp.isMember("errorCode") && http_resp.isMember("username") && http_resp.isMember("currency") && http_resp.isMember("balance")))
					{
						err1 = API_AUTH_FAILED;
						log_error("json param wrong, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}
					if (!(http_resp["errorCode"].isInt() && http_resp["username"].isString() && http_resp["currency"].isString() && http_resp["balance"].isDouble()))
					{
						err1 = API_AUTH_FAILED;
						log_error("json param wrong, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}
					if (0 != http_resp["errorCode"].asInt())
					{
						log_error("auth failed,%d", http_resp["errorCode"].asInt());
						err1 = API_AUTH_FAILED;
						break;
					}

					std::string username = http_resp["username"].asString();
					std::string currency1 = http_resp["currency"].asString();
					double balance = http_resp["balance"].asDouble();
					if (0 != currency1.compare(currency))
					{
						err1 = API_AUTH_FAILED;
						log_error("currency not match,currency1:%s,currency:%s", currency1.c_str(), currency.c_str());
						break;
					}
					if (username.empty())
					{
						err1 = API_AUTH_FAILED;
						log_error("username empty, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}

					if (!ValidUserName(username))
					{
						err1 = API_AUTH_FAILED;
						log_error("username not valid, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}

					if (currency_unit_multi == 0)
					{
						err1 = FAILED;
						log_error("currency_unit_multi is 0, body:%s ,url:%s,param:%s", body.c_str(), url1.c_str(), json_string.c_str());
						break;
					}

					std::string newtoken = token;
					if (http_resp.isMember("token") && http_resp["token"].isString())
					{
						std::string token1 = http_resp["token"].asString();
						if (!token1.empty())
						{
							newtoken = token1;
						}
					}

					std::int64_t llbalance = balance * currency_unit_multi;
					// 获取游戏信息

					std::string gameinfo = "";
					{
						Json::Value json;
						json["fullscreen"] = fullscreen;
						json["lang"] = lang;
						json["extern_json"] = extern_json;
						gameinfo = CTools::ToStr(json);
					}

					StreamData stream_get_token;
					StreamDataWriter write_get_token(stream_get_token);
					write_get_token.WriteInt(request_id);
					write_get_token.WriteInt64(request_time);
					write_get_token.WriteString(lauch_url);
					write_get_token.WriteString(agent_url);
					write_get_token.WriteInt(gameid);
					write_get_token.WriteInt(display_mode);
					write_get_token.WriteString(lang);

					if (!AccountService::Instance()->HandleGetToken(agency, agengcy_id, username, newtoken, currency, 
						homeurl, platform, llbalance, gameid, gameinfo, std::move(stream_get_token.Data()), AccountService::ACCESS_LOGIN))
					{
						log_error("HandleUpdateUser failed, game:%u, gameinfo:%s", gameid, gameinfo.c_str());
						err1 = FAILED;
						break;
					}
					return;
				} while (false);

				ResponseLogin(request_id, request_time, err1);
			});
		return;
    } while (false);
    ResponseLogin(request_id, request_time, err);
}

void ActionLogin::OnGenLoginToken(account_proto::GenLoginTokenResp&& get_token_resp)
{
    log_debug("%s", get_token_resp.ShortDebugString().c_str());

    Error err = SUCCESS;

    StreamData stream_data(std::move(get_token_resp.trans()));
    StreamDataReader reader(stream_data);
    std::uint32_t request_id = reader.ReadInt();
	std::uint64_t request_time = reader.ReadInt64();
    std::string lauch_url = reader.ReadString();
    std::string agent_url = reader.ReadString();
    std::uint32_t game = reader.ReadInt();
    std::uint32_t display_mode = reader.ReadInt();
    std::string lang = reader.ReadString();
    
    do
    {
        if (get_token_resp.err_code() != 0)
        {
            log_error("get token failed,%s", get_token_resp.ShortDebugString().c_str());
            err = FAILED; 
            break;
        }
        
        char agent[1024] = {0};
        Base64encode(agent, agent_url.c_str(), agent_url.length());

        std::stringstream url;
        url << lauch_url;
		if (std::string::npos == lauch_url.find('?'))
		{
			url << "?"; 
		}
		else
		{
			url << "&"; 
		}
        url << "token=" << get_token_resp.token() 
            << "&agent=" << agent
            << "&gameID=" << game
            << "&DisplayMode=" << display_mode
			<< "&lang=" << lang
			<< "&uid=" << get_token_resp.uid()  // 新增用户uid,方便支持nginx的负载均衡
			;
            
        ResponseLogin(request_id, request_time, err, url.str(), display_mode);
        return ;
    } while (false);
    ResponseLogin(request_id, request_time, err);
}

bool ActionLogin::ValidUserName(const std::string& username)
{
	for (char ch : username)
	{
		if (ch >= 'a' && ch <= 'z') continue;
		if (ch >= 'A' && ch <= 'Z') continue;
		if (ch >= '0' && ch <= '9') continue;
		if (ch == '_') continue;
		return false;
	}
	return true;
}






















